﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using RpgLibrary;
using RpgLibrary.WorldClasses;
using RpgLibrary.CharacterClasses;

using XRpgLibrary;
using XRpgLibrary.ExtensionMethods;
using XRpgLibrary.TileEngine;
using XRpgLibrary.ScriptClasses;


namespace XRpgLibrary.CharacterClasses
{
    public class CharacterManager : IGameDrawable, IGameUpdateable
    {
        public bool AllCharactersInteract = true;
        Character player = null;
        SortableBindingList<Character> characters;
        DrawOrderCharacterComparer comparer;
        
        public SortableBindingList<Character> Characters 
        {
            get
            {
                if (characters == null)
                    characters = new SortableBindingList<Character>();

                return characters;
            }
            set
            {
                characters = value;
            }
        }

        public PassTile[,] PassMap { get; set; }

        public Character this[string name]
        {
            get
            {
                return GetEntityByName(name);
            }
            set
            {
                Character c = GetEntityByName(name);
                c = value;
            }
        }

        public CharacterManager()
            : this (new List<Character>(), null)
        { }

        public CharacterManager(PassTile[,] passLayer)
            : this(new List<Character>(), passLayer)
        { }

        public CharacterManager(List<Character> characters)
            : this (characters, null)
        { }

        public CharacterManager(List<Character> characters, PassTile[,] passMap)
        {
            this.characters = new SortableBindingList<Character>(characters);
            this.comparer = new DrawOrderCharacterComparer();
            this.PassMap = passMap;
        }

        public void Draw(SpriteBatch renderTargetSpriteBatch)
        {
            for (int i = 0, cnt = Characters.Count; i < cnt; i++)
                characters[i].Draw(renderTargetSpriteBatch);
        }

        public void Draw(GameTime gameTime)
        {
            Draw(gameTime, null);
        }
        
        public void Draw(GameTime gameTime, Camera camera)
        {
            for (int i = 0, cnt = Characters.Count; i < cnt; i++)
                characters[i].Draw(gameTime);
        }

        public void Update(GameTime gameTime)
        {
            List<Character> deleteList = new List<Character>();
            foreach(Character c in Characters)
            {
                if (c.IsAlive)
                    c.Update(gameTime);
                else
                    deleteList.Add(c);
            }

            foreach (Character del in deleteList)
                Characters.Remove(del);

            Characters.Sort(comparer);
            if(PassMap != null)
                DoPassabilityCollisions(PassMap, gameTime);

            if (AllCharactersInteract)
                DoEntityCollisions(gameTime);
            else
                DoPlayerCollisions(gameTime);
        }

        private void DoPassabilityCollisions(PassTile[,] passMap, GameTime gameTime)
        {
            List<Rectangle> rectList;

            foreach(Character c in characters)
            {
                Point topLeftTile = Engine.VectorToCell(c.Sprite.Position);

                for (int y = topLeftTile.Y, ycnt = y + c.TileSpanY; y < ycnt; y++)
                    for (int x = topLeftTile.X, xcnt = x + c.TileSpanX; x < xcnt; x++)
                    {
                        rectList = 
                            passMap[
                                Math.Min(y, passMap.GetLength(0) - 1), 
                                Math.Min(x, passMap.GetLength(1) - 1)]
                                    .PassRects;
                        
                        if (rectList != null)
                            foreach(Rectangle r in rectList)
                            {
                                Vector4 passRect = new Vector4(
                                    r.X + x * Engine.TileWidth,
                                    r.Y + y * Engine.TileHeight,
                                    r.Width,
                                    r.Height);

                                Vector2 intersectionVector = passRect.IntersectionRepelVector(c.CollisionBox);
                                if (intersectionVector != Vector2.Zero)
                                    c.DoCollideWall(passRect, intersectionVector, gameTime);
                            }
                    }
            }
        }

        public void DoEntityCollisions(GameTime gameTime)
        {
            //if I could sort by position to get the closest entities, 
            //I could check only the nearby entities instead of all entities 
            //on the map - would the sorting be more expensive than just
            //checking them all?
            
            for (int i = 0, cnt = characters.Count; i < cnt; i++)
                for (int j = 0, jcnt = characters.Count; j < jcnt; j++)
                {
                    if (i != j)
                    {
                        if (characters[i].CollisionBox.Intersects(characters[j].CollisionBox))
                        {
                            characters[i].DoColliding(characters[j], gameTime);
                        }
                        else
                        {
                            characters[i].DoCollided(characters[j], gameTime);
                        }
                    }
                }
        }

        private void DoPlayerCollisions(GameTime gameTime)
        {
            if (player == null)
                return;

            foreach(Character c in characters)
                if (!c.IsPlayer)
                {
                    if (c.CollisionBox.Intersects(player.CollisionBox))
                        c.DoColliding(player, gameTime);
                    else
                        c.DoCollided(player, gameTime);
                }
        }

        public void AddPlayer(Character c)
        {
            if (!Characters.Contains(c))
                Characters.Add(c);
            
            player = c;
        }

        public void RemovePlayer(Character c)
        {
            if (Characters.Contains(c))
                Characters.Remove(c);

            player = null;
        }

        public void SetPlayer(Character c)
        {
            AddPlayer(c);
        }

        internal static CharacterManager FromCharacterData(
            CharacterData[] characters, 
            SpriteBatch spriteBatch, 
            ContentManager contentManager,
            ScriptContext scriptContext)
        {
            CharacterManager characterManager = new CharacterManager();

            characterManager.Characters = new SortableBindingList<Character>();

            if (characters != null)
            {
                foreach (CharacterData data in characters)
                    characterManager.Characters.Add(Character.FromCharacterData(data, spriteBatch, contentManager, scriptContext));
            }

            return characterManager;
        }

        public string GetUniqueEntityName()
        {
            return GetUniqueEntityName("Character");
        }

        public string GetUniqueEntityName(string name)
        {
            name = name.TrimEnd(new char[] {' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}) + " ";
            int postNum = 0;

            for (int i = 0, cnt = Characters.Count; i < cnt; i++)
                if (Characters[i].Entity.EntityName == name + postNum.ToString())
                {
                    postNum++;
                    i = -1;
                }

            return name + postNum.ToString();            
        }

        public bool HasEntity(string name)
        {
            for (int i = 0, cnt = Characters.Count; i < cnt; i++)
                if (Characters[i].Entity.EntityName == name)
                    return true;

            return false;
        }

        public Character GetEntityByName(string name)
        {
            for (int i = 0, cnt = Characters.Count; i < cnt; i++)
                if (Characters[i].Entity.EntityName == name)
                    return Characters[i];

            return null;
        }

        public Character GetEntityByPosition(Vector2 position)
        {
            return GetCharacterByPosition((int)position.X, (int)position.Y);
        }

        public Character GetCharacterByPosition(int x, int y)
        {
            int xtolerance = Engine.TileWidth / 2;
            int ytolerance = Engine.TileHeight / 2;

            for (int i = 0, cnt = Characters.Count; i < cnt; i++)
                if (Characters[i].CollisionBox.Intersects(new Vector4(
                    x - xtolerance / 2,
                    y - ytolerance / 2,
                    xtolerance,
                    ytolerance)))
                    return Characters[i];

            return null;
        }

        public void UpdateEntity(string name, Character character)
        {
            Character oldCharacter = GetEntityByName(name);
            if (oldCharacter != null)
            {
                Characters.Remove(oldCharacter);
                Characters.Add(character);
            }
        }

        
    }
}
